﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using GraphProcessor;
using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UIElements;

namespace IQIGame.Onigao.GamePlay.Editors
{
    [NodeCustomEditor(typeof(LevelServiceBaseNode))]
    public class LevelServiceBaseNodeView : BaseNodeView
    {
        protected override VisualElement AddControlField(FieldInfo field, string label = null, bool showInputDrawer = false, Action valueChangedCallback = null)
        {
            if (field == null)
                return null;

            var rProperty = FindSerializedProperty(field.Name);
            var element = new PropertyField(rProperty, showInputDrawer ? "" : label);
            element.Bind(owner.serializedGraph);

#if UNITY_2020_3 // In Unity 2020.3 the empty label on property field doesn't hide it, so we do it manually
			if ((showInputDrawer || String.IsNullOrEmpty(label)) && element != null)
				element.AddToClassList("DrawerField_2020_3");
#endif

            if (typeof(IList).IsAssignableFrom(field.FieldType))
            {
                EnableSyncSelectionBorderHeight();
            }

            element.RegisterValueChangeCallback(e =>
            {
                UpdateFieldVisibility(field.Name, field.GetValue(nodeTarget));
                valueChangedCallback?.Invoke();
                NotifyNodeChanged();
            });

            propertyChangeBindMap.TryAdd(owner.serializedGraph, null);
            propertyChangeBindMap[owner.serializedGraph] += () => UpdateFieldVisibility(field.Name, field.GetValue(nodeTarget));

            // Disallow picking scene objects when the graph is not linked to a scene
            if (element != null && !owner.graph.IsLinkedToScene())
            {
                var objectField = element.Q<ObjectField>();
                if (objectField != null)
                    objectField.allowSceneObjects = false;
            }

            if (!fieldControlsMap.TryGetValue(field, out var inputFieldList))
                inputFieldList = fieldControlsMap[field] = new List<VisualElement>();
            inputFieldList.Add(element);

            if (element != null)
            {
                if (showInputDrawer)
                {
                    var box = new VisualElement { name = field.Name };
                    box.AddToClassList("port-input-element");
                    box.Add(element);
                    inputContainerElement.Add(box);
                }
                else
                {
                    controlsContainer.Add(element);
                }
                element.name = field.Name;
            }
            else
            {
                // Make sure we create an empty placeholder if FieldFactory can not provide a drawer
                if (showInputDrawer) AddEmptyField(field, false);
            }

            var visibleCondition = field.GetCustomAttribute(typeof(VisibleIf)) as VisibleIf;
            if (visibleCondition != null)
            {
                // Check if target field exists:
                var conditionField = nodeTarget.GetType().GetField(visibleCondition.fieldName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
                if (conditionField == null)
                    Debug.LogError($"[VisibleIf] Field {visibleCondition.fieldName} does not exists in node {nodeTarget.GetType()}");
                else
                {
                    visibleConditions.TryGetValue(visibleCondition.fieldName, out var list);
                    if (list == null)
                        list = visibleConditions[visibleCondition.fieldName] = new List<(object value, VisualElement target)>();
                    list.Add((visibleCondition.value, element));
                    UpdateFieldVisibility(visibleCondition.fieldName, conditionField.GetValue(nodeTarget));
                }
            }

            if (typeof(IList).IsAssignableFrom(field.FieldType))
            {
                if (field.GetCustomAttribute(typeof(LevelServiceEditorCustomPropertyAttribute)) is LevelServiceEditorCustomPropertyAttribute rCustomAttr)
                {
                    var rConfig = LevelServiceEditorSettingGlobal.GetConfig(rCustomAttr.key);
                    if (rConfig != null && (rConfig.type & LevelServiceEditorSetting.ELevelServiceEditorConfigType.PickCoordinate) > 0)
                    {
                        Button rPickBtn = new Button();
                        rPickBtn.text = "批量拾取坐标";
                        rPickBtn.clicked +=
                            () => { LevelServiceBlackboard.Instance.SwitchPick(rProperty); };
                        controlsContainer.Add(rPickBtn);
                        Button rClearBtn = new Button();
                        rClearBtn.text = "清理坐标";
                        rClearBtn.clicked += () =>
                        {
                            rProperty.ClearArray();
                            rProperty.serializedObject.ApplyModifiedProperties();
                            LevelServiceBlackboard.Instance.StopPick();
                        };
                        controlsContainer.Add(rClearBtn);

                        schedule.Execute(() => { rPickBtn.style.backgroundColor = LevelServiceBlackboard.Instance.pickProperty == rProperty ? new StyleColor(Color.red) : new StyleColor(StyleKeyword.Null); }).Every(17);
                    }
                }
            }
            return element;
        }

        public static Dictionary<SerializedObject, Action> propertyChangeBindMap = new Dictionary<SerializedObject, Action>();

        public static void OnPropertyChange(SerializedObject serializedObject)
        {
            if (propertyChangeBindMap.TryGetValue(serializedObject, out var rAction))
            {
                rAction?.Invoke();
            }
        }

        public override void BuildContextualMenu(ContextualMenuPopulateEvent evt)
        {
            BuildAlignMenu(evt);
            evt.menu.AppendAction("Open Node Script", (e) => OpenNodeScript(), OpenNodeScriptStatus);
            evt.menu.AppendAction("Open Node View Script", (e) => OpenNodeViewScript(), OpenNodeViewScriptStatus);
            evt.menu.AppendAction("Debug", (e) => ToggleDebug(), DebugStatus);
            if (nodeTarget.unlockable)
                evt.menu.AppendAction((nodeTarget.isLocked ? "Unlock" : "Lock"), (e) => ChangeLockStatus(), LockStatus);
            evt.menu.AppendAction("CopyGUID", (e) => CopyGuid(), CopyGuidStatus);
            evt.menu.AppendAction("同步服务执行", (e) => SyncExecutionOnNode(), SyncExecutionStatus);
            evt.menu.AppendAction("查找所有服务", (e) => FindAllServiceByExecution(), FindAllServiceStatus);
        }

        private void CopyGuid()
        {
            // 复制GUID
            EditorGUIUtility.systemCopyBuffer = IDGenerateNode.LondIDToInt2(this.nodeTarget.GUIDLong).ToString();
        }

        private void SyncExecutionOnNode()
        {
            if (this.nodeTarget is LevelServiceExecutionNode rExeNode)
            {
                LevelServiceBlackboard.Instance.SyncExecutionOnNode(rExeNode);
            }
        }
        
        private void FindAllServiceByExecution()
        {
            if (this.nodeTarget is LevelServiceExecutionNode rExeNode)
            {
                LevelServiceBlackboard.Instance.FindAllServiceByExecution(rExeNode);
            }
        }
        
        private DropdownMenuAction.Status CopyGuidStatus(DropdownMenuAction action)
        {
            if (nodeTarget.GUIDLong == 0)
                return DropdownMenuAction.Status.Hidden;
            return DropdownMenuAction.Status.Normal;
        }
        
        private DropdownMenuAction.Status SyncExecutionStatus(DropdownMenuAction action)
        {
            if (this.nodeTarget is LevelServiceExecutionNode)
                return DropdownMenuAction.Status.Normal;
            return DropdownMenuAction.Status.Hidden;
        }
        
        private DropdownMenuAction.Status FindAllServiceStatus(DropdownMenuAction action)
        {
            if (this.nodeTarget is LevelServiceExecutionNode)
                return DropdownMenuAction.Status.Normal;
            return DropdownMenuAction.Status.Hidden;
        }
    }
}